package com.ygqh.baby.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ygqh.baby.ao.*;
import com.ygqh.baby.exception.YgHttpException;
import com.ygqh.baby.http.YgRequestExecutor;
import com.ygqh.baby.http.YgResponse;
import com.ygqh.baby.mapper.ScheduleJobMapper;
import com.ygqh.baby.model.PromotionTag;
import com.ygqh.baby.model.SchJob;
import com.ygqh.baby.po.ScheduleJob;
import com.ygqh.baby.po.ScheduleJobExample;
import com.ygqh.baby.po.YgOrder;
import com.ygqh.baby.service.ScheduleJobService;
import com.ygqh.baby.utils.DateConvertUtils;
import com.ygqh.baby.utils.MailUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service("scheduleJobService")
public class ScheduleJobServiceImpl implements ScheduleJobService {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private ScheduleJobMapper scheduleJobMapper;
    @Autowired
    private MailUtil mailUtil;
    // @Value("${project.type}")
    private String jobType = "DWH";
    @Value("${job.api}")
    private String jobApi;

    private final static ExecutorService executorService = Executors.newFixedThreadPool(3);
    private static final String[] to = new String[]{"guohao@yiigoo.com", "sunshuo@yiigoo.cn"};

    @Override
    public List<ScheduleJob> find() {
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andStatusEqualTo(DataStatus.Valid.name());
        return scheduleJobMapper.selectByExample(example);
    }

    @Override
    public int findCount() {
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andStatusEqualTo(DataStatus.Valid.name());
        return scheduleJobMapper.countByExample(example);
    }

    @Override
    public ScheduleJob findById(Long id) {
        return scheduleJobMapper.selectByPrimaryKey(id);
    }

    @Override
    public int save(ScheduleJob scheduleJob) {
        return scheduleJobMapper.insertSelective(scheduleJob);
    }

    @Override
    public int update(ScheduleJob scheduleJob) {
        return scheduleJobMapper.updateByPrimaryKeySelective(scheduleJob);
    }

    @Override
    public ResultSet<ScheduleJob> search(QueryInfo queryInfo, String q, String groupName, Date startDate, Date endDate) {
        List<ScheduleJob> list = scheduleJobMapper.selectSuper(queryInfo, q, groupName, startDate, endDate);
        int count = scheduleJobMapper.countSuper(q, groupName, startDate, endDate);
        ResultSet<ScheduleJob> resultSet = new ResultSet<ScheduleJob>(count, list);
        return resultSet;
    }

    @Override
    public int deleteSoft(Long[] ids) {
        Assert.notEmpty(ids);
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andIdIn(Arrays.asList(ids));
        ScheduleJob record = new ScheduleJob();
        record.setStatus(DataStatus.Delete);

        return scheduleJobMapper.updateByExampleSelective(record, example);
    }

    @Override
    public int deleteSoft(Long id) {
        return this.deleteSoft(new Long[]{id});
    }

    @Override
    public int saveOrUpdate(ScheduleJob scheduleJob) {
        int r = 0;
        if (scheduleJob.getId() != null) {
            r = this.update(scheduleJob);
        } else {
            r = this.save(scheduleJob);
        }
        return r;
    }

    @Override
    public List<ScheduleJob> findJobs(String jobStatus, String jobType, String ipAddr, Boolean hasNum) {
        return scheduleJobMapper.selectJobs(jobStatus, jobType, ipAddr, hasNum);
    }

    @Override
    public Boolean exitJob(String jobName, String jobGroup) {
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andStatusEqualTo(DataStatus.Valid.name()).andJobNameEqualTo(jobName).andJobGroupEqualTo(jobGroup);
        List<ScheduleJob> list = scheduleJobMapper.selectByExample(example);
        if (CollectionUtils.isNotEmpty(list)) {
            return true;
        }
        return false;
    }

    @Override
    public ScheduleJob getJob(String jobName, String jobGroup) {
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andStatusEqualTo(DataStatus.Valid.name()).andJobNameEqualTo(jobName).andJobGroupEqualTo(jobGroup);
        List<ScheduleJob> list = scheduleJobMapper.selectByExample(example);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public int updateCron(Long jobId, String cron) {
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andIdEqualTo(jobId).andStatusEqualTo(DataStatus.Valid.name());
        ScheduleJob record = new ScheduleJob();
        record.setCron(cron);
        int i = scheduleJobMapper.updateByExampleSelective(record, example);

        return i;
    }

    @Override
    public Message addJobToSchedule(ScheduleJob scheduleJob) {

        return this.httpToSchedule(scheduleJob, "addJob");
    }

    private Message httpToSchedule(ScheduleJob scheduleJob, String method) {
        Map<String, String> data = new HashMap<String, String>();
        data.put("method", method);
        data.put("job", JSON.toJSONString(scheduleJob));
        Message msg = httpToSchedule(data);
        if (msg.getType().equals(Message.Type.error)) {
            this.sendErrorMail(scheduleJob, msg.getContent());
        }
        return msg;
    }

    private Message httpToSchedule(Map<String, String> param) {
        Message msg;
        try {
            YgRequestExecutor request = new YgRequestExecutor();
            YgResponse response = request.post(jobApi, param);
            msg = response.getAsObject(new TypeReference<Message>() {
            });
        } catch (YgHttpException e) {
            logger.error(JSON.toJSONString(param), e);
            msg = Message.error(e.getMessage());
            e.printStackTrace();
        }
        return msg;
    }

    @Override
    public Message updateJobToSchedule(ScheduleJob scheduleJob) {

        return this.httpToSchedule(scheduleJob, "updateJobCron");
    }

    @Override
    public Message deleteJobToSchedule(ScheduleJob scheduleJob) {

        return this.httpToSchedule(scheduleJob, "delJob");
    }

    @Override
    public int updateCronByJobKey(String jobName, String jobGroup, String cron, Date cronDate, String handler) {
        return updateCronByJobKey(jobName, jobGroup, cron, cronDate, handler, 1);
    }

    private int updateCronByJobKey(String jobName, String jobGroup, String cron, Date cronDate, String handler, Integer jobNum) {
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andStatusEqualTo(DataStatus.Valid.name()).andJobNameEqualTo(jobName).andJobGroupEqualTo(jobGroup);
        ScheduleJob record = new ScheduleJob();
        record.setCron(cron);
        record.setCronDate(cronDate);
        record.setJobStatus(SchJob.STATUS_RUNNING);
        record.setJobNum(jobNum);
        record.setUpdateTime(new Date());
        record.setUpdateBy(handler);

        int i = scheduleJobMapper.updateByExampleSelective(record, example);
        return i;
    }

    @Override
    public Message addBalanceJob(Long orderId, Date sendTime, String handler) {

        Date runDate = DateConvertUtils.addDay(sendTime, 15);
        Date currentDate = new Date();
        if (runDate.before(currentDate)) {
            runDate = DateConvertUtils.addMinute(currentDate, 5);
        }
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("orderId", orderId);

        return this.addJob(YgQuartzJob.Balance, orderId.toString(), runDate, dataMap, handler);

    }

    @Override
    public Message addTrustonePushOrderJob(String orderCode, Date excuteTime, String handler) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("orderCode", orderCode);
        return this.addJob(YgQuartzJob.TrustoneAddOrder, orderCode, excuteTime, dataMap, handler);
    }

    @Override
    public Message addJob(YgQuartzJob quartzJob, String name, Date excuteTime, Map<String, Object> dataMap, String handler) {
        Message message;
        try {
            message = this.addJob(quartzJob, name, excuteTime, dataMap, handler, 1);
        } catch (Exception e) {
            logger.error("添加定时任务失败", e);
            message = Message.error("添加定时任务失败", null);
        }
        return message;
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public Message addJob(YgQuartzJob quartzJob, String name, Date excuteTime, Map<String, Object> dataMap, String handler, Integer jobTimes) {
        String jobName = quartzJob.getJobName(name);
        String jobGroup = quartzJob.getJobGroup();
        ScheduleJob job = this.getJob(jobName, jobGroup);
        Date currentDate = new Date();
        if (excuteTime.before(currentDate)) {
            excuteTime = DateConvertUtils.addMinute(currentDate, 5);
        }
        String cron = DateConvertUtils.format(excuteTime, "ss mm HH dd MM ? yyyy");
        Message msg;
        if (job != null) {
            job.setCron(cron);
            job.setCronDate(excuteTime);
            msg = this.updateJobToSchedule(job);
            if (msg.getType().equals(Message.Type.success)) {
                this.updateCronByJobKey(jobName, jobGroup, cron, excuteTime, handler, jobTimes);
            }
        } else {
            job = new SchJob(jobName, jobGroup, jobType, jobTimes, cron, quartzJob.getDesc(), quartzJob.getSpringId(), quartzJob.getMethodName(), dataMap);
            job.setCreateTime(new Date());
            job.setCreateBy(handler);
            job.setCronDate(excuteTime);
            msg = this.addJobToSchedule(job);
            if (msg.getType().equals(Message.Type.success)) {
                int save = this.save(job);
                if (save != 1) {
                    msg = Message.error("插入数据库失败", null);
                }
            }
        }
        return msg;
    }

    @Override
    public void addBalanceJobBatch(List<YgOrder> orderList, String handler) {

        for (YgOrder ygOrder : orderList) {
            executorService.execute(() -> addBalanceJob(ygOrder.getOrderId(), ygOrder.getSendTime(), handler));

        }

    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public Message addBalanceJobByRof(Long orderId, String trueName) {
        YgQuartzJob qj = YgQuartzJob.Balance;
        String jobName = qj.getJobName(orderId.toString());
        String jobGroup = qj.getJobGroup();
        ScheduleJob balanceJob = this.getJob(jobName, jobGroup);
        if (balanceJob != null) {
            String oldCron = balanceJob.getCron();
            Date runDate = DateConvertUtils.parse(oldCron, DateConvertUtils.DATE_FORMAT_CRON);
            Date currentDate = new Date();
            if (runDate.before(currentDate)) {
                runDate = currentDate;
            }
            runDate = DateConvertUtils.addMinute(runDate, 15);
            String newCron = DateConvertUtils.format(runDate, "ss mm HH dd MM ? yyyy");
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("orderId", orderId);
            SchJob job = new SchJob(jobName, jobGroup, jobType, 1, newCron, "预收入转可提现", qj.getSpringId(), qj.getMethodName(), dataMap);

            Message msg = null;
            if (balanceJob.getJobStatus().equals(SchJob.STATUS_RUNNING)) {
                job.setUpdateTime(new Date());
                job.setUpdateBy("addByRof");
                msg = this.updateJobToSchedule(job);
            } else {
                job.setCreateTime(new Date());
                job.setCreateBy("addByRof");
                msg = this.addJobToSchedule(job);
            }
            if (msg.getType().equals(Message.Type.success)) {

                this.updateCronByJobKey(jobName, jobGroup, newCron, runDate, "addByRof");
            }
            return msg;
        } else {
            return Message.success(null);
        }

    }

    @Override
    public void sendErrorMail(ScheduleJob scheduleJob, String msg) {

        StringBuffer str = new StringBuffer();
        str.append(
                "<html><head><style>.table-d table{ background:#000;font-size:14px} .table-d table td{ background:#FFF} h5{width: auto;margin-right: 20px} .title{display: flex;line-height: 10px}");
        str.append("</style></head><body>");
        str.append("<div class='table-d'>");
        str.append("<table width='800' border='0' cellspacing='1' cellpadding='0'><tr><td width='15%'>任务名称</td><td width='15%'>失败原因</td></tr>");
        str.append("<tr><td>").append(scheduleJob.getJobName()).append("</td>").append("<td>").append(msg).append("</td></tr></div></body></html>");

        try {
            mailUtil.sendWithHtml(to, "定时服务异常通知", str.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SchJob> getAllJobFromSchedule() {
        YgRequestExecutor request = new YgRequestExecutor();
        Map<String, String> data = new HashMap<String, String>();
        data.put("method", "getAllJob");

        try {
            YgResponse response = request.post(jobApi, data);
            Message message = response.getAsObject(new TypeReference<Message>() {
            });

            return (List<SchJob>) message.getExtra();
        } catch (YgHttpException e) {
            e.printStackTrace();
        }
        return null;

    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public Message addCancelAgentJob(Long agentId, Date updateTime, String cancelBy) {
        YgQuartzJob qj = YgQuartzJob.CancelAgent;
        String jobName = qj.getJobName(agentId.toString());
        String jobGroup = qj.getJobGroup();
        ScheduleJob scheduleJob = this.getJob(jobName, jobGroup);
        Date runTime = DateConvertUtils.addDay(updateTime, 5);
        String newCron = DateConvertUtils.format(runTime, "ss mm HH dd MM ? yyyy");
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("agentId", agentId);
        dataMap.put("cancelBy", cancelBy);
        SchJob job = new SchJob(jobName, jobGroup, jobType, 1, newCron, "代理资格取消", qj.getSpringId(), qj.getMethodName(), dataMap);
        job.setCreateTime(new Date());
        job.setCreateBy(cancelBy);
        job.setCronDate(runTime);
        if (scheduleJob != null) {
            this.updateCronByJobKey(jobName, jobGroup, newCron, runTime, cancelBy);
        } else {
            this.save(job);
        }
        return this.addJobToSchedule(job);
    }

    @Override
    public Message deleteJob(String jobName, String jobGroup, String handler) {
        Message msg;
        try {
            ScheduleJob scheduleJob = this.getJob(jobName, jobGroup);
            scheduleJob.setJobStatus(SchJob.STATUS_NOT_RUNNING);
            scheduleJob.setJobNum(0);
            scheduleJob.setUpdateBy(handler);
            scheduleJob.setUpdateTime(new Date());
            msg = this.deleteJobToSchedule(scheduleJob);
            if (msg.getType().equals(Message.Type.success)) {
                scheduleJobMapper.updateByPrimaryKeySelective(scheduleJob);
            }
        } catch (Exception e) {
            logger.error("deleteJob fail : {}", e.getMessage(), e);
            msg = Message.error("deleteJob fail : " + e.getMessage());
        }

        return msg;
    }

    @Override
    public List<ScheduleJob> findJobByJobNames(String... jobNames) {
        if (jobNames == null || jobNames.length == 0) {
            return null;
        }
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andJobNameIn(Arrays.asList(jobNames)).andStatusEqualTo(DataStatus.Valid.name());
        return scheduleJobMapper.selectByExample(example);
    }

    @Override
    public int updateByJobKey(ScheduleJob scheduleJob) {
        ScheduleJobExample example = new ScheduleJobExample();
        example.createCriteria().andJobNameEqualTo(scheduleJob.getJobName()).andJobGroupEqualTo(scheduleJob.getJobGroup())
                .andStatusEqualTo(DataStatus.Valid.name());
        return scheduleJobMapper.updateByExampleSelective(scheduleJob, example);
    }

    @Override
    public Message updatePromotionTag(Long relateId, PromotionTag promotionTag) {

        Map<String, Object> param = new HashMap<>();
        param.put("relateId", relateId);
        param.put("promotionTag", promotionTag);
        return this.addJob(YgQuartzJob.PromotionTag, promotionTag.getPromotionType() + "_" + promotionTag.getPromotionId(), promotionTag.getResetTime(), param, promotionTag.getUpdateBy());
    }

    @Override
    public List<ScheduleJob> findJobList(String groupName, Boolean hasNum) {
        return scheduleJobMapper.selectJobList(groupName, hasNum);
    }

    @Override
    public List<ScheduleJob> getJobFromQuartz(String groupName, String jobName) {
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobGroup(groupName);
        scheduleJob.setJobName(jobName);
        Message getAllJob = this.httpToSchedule(scheduleJob, "getAllJob");
        return (List<ScheduleJob>) getAllJob.getExtra();
    }


}
